package snowflake

import (
	"errors"
	"github.com/bwmarrin/snowflake"
	"net"
)

const (
	DataCenterIdMax = 3
	NodeIdMax       = 1<<8 - 1
)

var (
	ErrNoPrivateAddress    = errors.New("no private ip address")
	ErrNodeId              = errors.New("nodeId is out of range")
	ErrDataCenterId        = errors.New("dataCenterId is out of range")
	defaultInterfaceAdders = net.InterfaceAddrs
)

type Node struct {
	node *snowflake.Node
}

func (n *Node) Next() snowflake.ID {
	return n.node.Generate()
}

// NewDefaultNode
// Use the last 8 bits of the local IP address as the node ID
// Supports up to 256 nodes
func NewDefaultNode() (*snowflake.Node, error) {
	nodeId, err := lower8BitPrivateIP(defaultInterfaceAdders)
	if err != nil {
		return nil, err
	}

	return snowflake.NewNode(int64(nodeId))
}

// New10BitsNode
// the requirements for local IP addresses is
// x.x.[0-3].0/24 ,for example: 192.168.0.1~192.168.3.255
// If your application is in k8s，set --cluster-cidr
// for example you can set up to 4 clusters, and each cluster must
// be set with a cidr. For example, one cluster is 192.168.0.0/24, the second is 192.168.1.0/24,
// the third is 192.168.2.0/24, and the fourth is 192.168.3.0/24
// Supports up to 1024 nodes
func New10BitsNode() (*snowflake.Node, error) {
	nodeId, err := lower10BitPrivateIP(defaultInterfaceAdders)
	if err != nil {
		return nil, err
	}

	return snowflake.NewNode(int64(nodeId))
}

// NewNode
// You can manually specify dataCenterId and nodeId
// dataCenterId is not allowed exceed 3
// Means supporting up to 3 data centers
func NewNode(dataCenterId int, nodeId int) (*snowflake.Node, error) {
	if dataCenterId > DataCenterIdMax {
		return nil, ErrDataCenterId
	}

	if nodeId > NodeIdMax {
		return nil, ErrNodeId
	}

	id := dataCenterId<<8 + nodeId
	return snowflake.NewNode(int64(id))
}

func privateIPv4(interfaceAdders InterfaceAddrs) (net.IP, error) {
	as, err := interfaceAdders()
	if err != nil {
		return nil, err
	}

	for _, a := range as {
		ipNet, ok := a.(*net.IPNet)
		if !ok || ipNet.IP.IsLoopback() {
			continue
		}

		ip := ipNet.IP.To4()
		if isPrivateIPv4(ip) {
			return ip, nil
		}
	}
	return nil, ErrNoPrivateAddress
}

func isPrivateIPv4(ip net.IP) bool {
	return ip != nil &&
		(ip[0] == 10 || ip[0] == 172 && (ip[1] >= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168)
}

func lower8BitPrivateIP(interfaceAdders InterfaceAddrs) (int, error) {
	ip, err := privateIPv4(interfaceAdders)
	if err != nil {
		return 0, err
	}

	return int(ip[3]), nil
}

func lower10BitPrivateIP(interfaceAdders InterfaceAddrs) (int, error) {
	ip, err := privateIPv4(interfaceAdders)
	if err != nil {
		return 0, err
	}

	if ip[2] > DataCenterIdMax {
		return 0, ErrDataCenterId
	}

	lower2Bit := ip[2] & DataCenterIdMax

	return int(lower2Bit) + int(ip[3]), nil
}
